<?php
/*
*------------------------------------------------------------
*                   BMP Image functions
*------------------------------------------------------------
*                      By JPEXS
*/

/*
*------------------------------------------------------------
*                    ImageBMP
*------------------------------------------------------------
*            - Creates new BMP file
*
*         Parameters:  $img - Target image
*                      $file - Target file to store
*                            - if not specified, bmp is returned
*
*           Returns: if $file specified - true if OK
                     if $file not specified - image data
*/
function imagebmp($img, $file = "", $RLE = 0) {
	$ColorCount = imagecolorstotal($img);

	$Transparent = imagecolortransparent($img);
	$IsTransparent = $Transparent != -1;

	if ($IsTransparent)
		$ColorCount--;

	if ($ColorCount == 0) {
		$ColorCount = 0;
		$BitCount = 24;
	}
	if (($ColorCount > 0) and ($ColorCount <= 2)) {
		$ColorCount = 2;
		$BitCount = 1;
	}
	if (($ColorCount > 2) and ($ColorCount <= 16)) {
		$ColorCount = 16;
		$BitCount = 4;
	}
	if (($ColorCount > 16) and ($ColorCount <= 256)) {
		$ColorCount = 0;
		$BitCount = 8;
	}

	$Width = imagesx($img);
	$Height = imagesy($img);

	$Zbytek = (4 - ($Width / (8 / $BitCount)) % 4) % 4;

	if ($BitCount < 24)
		$palsize = pow(2, $BitCount) * 4;

	$size = (floor($Width / (8 / $BitCount)) + $Zbytek) * $Height +54;
	$size += $palsize;
	$offset = 54 + $palsize;

	// Bitmap File Header
	$ret = 'BM'; // header (2b)
	$ret .= int_to_dword($size); // size of file (4b)
	$ret .= int_to_dword(0); // reserved (4b)
	$ret .= int_to_dword($offset); // byte location in the file which is first byte of IMAGE (4b)
	// Bitmap Info Header
	$ret .= int_to_dword(40); // Size of BITMAPINFOHEADER (4b)
	$ret .= int_to_dword($Width); // width of bitmap (4b)
	$ret .= int_to_dword($Height); // height of bitmap (4b)
	$ret .= int_to_word(1); // biPlanes = 1 (2b)
	$ret .= int_to_word($BitCount); // biBitCount = {1 (mono) or 4 (16 clr ) or 8 (256 clr) or 24 (16 Mil)} (2b)
	$ret .= int_to_dword($RLE); // RLE COMPRESSION (4b)
	$ret .= int_to_dword(0); // width x height (4b)
	$ret .= int_to_dword(0); // biXPelsPerMeter (4b)
	$ret .= int_to_dword(0); // biYPelsPerMeter (4b)
	$ret .= int_to_dword(0); // Number of palettes used (4b)
	$ret .= int_to_dword(0); // Number of important colour (4b)
	// image data

	$CC = $ColorCount;
	$sl1 = strlen($ret);
	if ($CC == 0)
		$CC = 256;
	if ($BitCount < 24) {
		$ColorTotal = imagecolorstotal($img);
		if ($IsTransparent)
			$ColorTotal--;

		for ($p = 0; $p < $ColorTotal; $p++) {
			$color = imagecolorsforindex($img, $p);
			$ret .= inttobyte($color["blue"]);
			$ret .= inttobyte($color["green"]);
			$ret .= inttobyte($color["red"]);
			$ret .= inttobyte(0); //RESERVED
		}

		$CT = $ColorTotal;
		for ($p = $ColorTotal; $p < $CC; $p++) {
			$ret .= inttobyte(0);
			$ret .= inttobyte(0);
			$ret .= inttobyte(0);
			$ret .= inttobyte(0); //RESERVED
		}
	}

	if ($BitCount <= 8) {
		for ($y = $Height -1; $y >= 0; $y--) {
			$bWrite = "";
			for ($x = 0; $x < $Width; $x++) {
				$color = imagecolorat($img, $x, $y);
				$bWrite .= decbinx($color, $BitCount);
				if (strlen($bWrite) == 8) {
					$retd .= inttobyte(bindec($bWrite));
					$bWrite = "";
				}
			}

			if ((strlen($bWrite) < 8) and (strlen($bWrite) != 0)) {
				$sl = strlen($bWrite);
				for ($t = 0; $t < 8 - $sl; $t++)
					$sl .= "0";
				$retd .= inttobyte(bindec($bWrite));
			}
			for ($z = 0; $z < $Zbytek; $z++)
				$retd .= inttobyte(0);
		}
	}

	if (($RLE == 1) and ($BitCount == 8)) {
		for ($t = 0; $t < strlen($retd); $t += 4) {
			if ($t != 0)
				if (($t) % $Width == 0)
					$ret .= chr(0) .
					chr(0);

			if (($t +5) % $Width == 0) {
				$ret .= chr(0) . chr(5) . substr($retd, $t, 5) . chr(0);
				$t += 1;
			}
			if (($t +6) % $Width == 0) {
				$ret .= chr(0) . chr(6) . substr($retd, $t, 6);
				$t += 2;
			} else {
				$ret .= chr(0) . chr(4) . substr($retd, $t, 4);
			}
		}
		$ret .= chr(0) . chr(1);
	} else {
		$ret .= $retd;
	}

	if ($BitCount == 24) {
		for ($z = 0; $z < $Zbytek; $z++)
			$Dopl .= chr(0);

		for ($y = $Height -1; $y >= 0; $y--) {
			for ($x = 0; $x < $Width; $x++) {
				$color = imagecolorsforindex($img, ImageColorAt($img, $x, $y));
				$ret .= chr($color["blue"]) . chr($color["green"]) . chr($color["red"]);
			}
			$ret .= $Dopl;
		}
	}

	if ($file != "") {
		$r = ($f = fopen($file, "w"));
		$r = $r and fwrite($f, $ret);
		$r = $r and fclose($f);
		return $r;
	} else {
		echo $ret;
	}
}

/*
*------------------------------------------------------------
*                    ImageCreateFromBmp
*------------------------------------------------------------
*            - Reads image from a BMP file
*
*         Parameters:  $file - Target file to load
*
*            Returns: Image ID
*/

function imagecreatefrombmp($file) {
	global $CurrentBit;

	$f = fopen($file, "r");
	$Header = fread($f, 2);

	if ($Header == "BM") {
		$Size = freaddword($f);
		$Reserved1 = freadword($f);
		$Reserved2 = freadword($f);
		$FirstByteOfImage = freaddword($f);

		$SizeBITMAPINFOHEADER = freaddword($f);
		$Width = freaddword($f);
		$Height = freaddword($f);
		$biPlanes = freadword($f);
		$biBitCount = freadword($f);
		$RLECompression = freaddword($f);
		$WidthxHeight = freaddword($f);
		$biXPelsPerMeter = freaddword($f);
		$biYPelsPerMeter = freaddword($f);
		$NumberOfPalettesUsed = freaddword($f);
		$NumberOfImportantColors = freaddword($f);

		if ($biBitCount < 24) {
			$img = imagecreate($Width, $Height);
			$Colors = pow(2, $biBitCount);
			for ($p = 0; $p < $Colors; $p++) {
				$B = freadbyte($f);
				$G = freadbyte($f);
				$R = freadbyte($f);
				$Reserved = freadbyte($f);
				$Palette[] = imagecolorallocate($img, $R, $G, $B);
			}

			if ($RLECompression == 0) {
				$Zbytek = (4 - ceil(($Width / (8 / $biBitCount))) % 4) % 4;

				for ($y = $Height -1; $y >= 0; $y--) {
					$CurrentBit = 0;
					for ($x = 0; $x < $Width; $x++) {
						$C = freadbits($f, $biBitCount);
						imagesetpixel($img, $x, $y, $Palette[$C]);
					}
					if ($CurrentBit != 0) {
						freadbyte($f);
					}
					for ($g = 0; $g < $Zbytek; $g++)
						freadbyte($f);
				}
			}
		}

		if ($RLECompression == 1) {//$BI_RLE8
			$y = $Height;

			$pocetb = 0;

			while (true) {
				$y--;
				$prefix = freadbyte($f);
				$suffix = freadbyte($f);
				$pocetb += 2;

				$echoit = false;

				if ($echoit)
					echo "Prefix: $prefix Suffix: $suffix<BR>";
				if (($prefix == 0) and ($suffix == 1))
					break;
				if (feof($f))
					break;

				while (!(($prefix == 0) and ($suffix == 0))) {
					if ($prefix == 0) {
						$pocet = $suffix;
						$Data .= fread($f, $pocet);
						$pocetb += $pocet;
						if ($pocetb % 2 == 1) {
							freadbyte($f);
							$pocetb++;
						}
					}
					if ($prefix > 0) {
						$pocet = $prefix;
						for ($r = 0; $r < $pocet; $r++)
							$Data .= chr($suffix);
					}
					$prefix = freadbyte($f);
					$suffix = freadbyte($f);
					$pocetb += 2;
					if ($echoit)
						echo "Prefix: $prefix Suffix: $suffix<BR>";
				}

				for ($x = 0; $x < strlen($Data); $x++) {
					imagesetpixel($img, $x, $y, $Palette[ord($Data[$x])]);
				}
				$Data = "";
			}
		}

		if ($RLECompression == 2) {//$BI_RLE4
			$y = $Height;
			$pocetb = 0;

			/*while(!feof($f))
			 echo freadbyte($f)."_".freadbyte($f)."<BR>";*/
			while (true) {
				//break;
				$y--;
				$prefix = freadbyte($f);
				$suffix = freadbyte($f);
				$pocetb += 2;

				$echoit = false;

				if ($echoit)
					echo "Prefix: $prefix Suffix: $suffix<BR>";
				if (($prefix == 0) and ($suffix == 1))
					break;
				if (feof($f))
					break;

				while (!(($prefix == 0) and ($suffix == 0))) {
					if ($prefix == 0) {
						$pocet = $suffix;

						$CurrentBit = 0;
						for ($h = 0; $h < $pocet; $h++)
							$Data .= chr(freadbits($f, 4));
						if ($CurrentBit != 0)
							freadbits($f, 4);
						$pocetb += ceil(($pocet / 2));
						if ($pocetb % 2 == 1) {
							freadbyte($f);
							$pocetb++;
						}
					}
					if ($prefix > 0) {
						$pocet = $prefix;
						$i = 0;
						for ($r = 0; $r < $pocet; $r++) {
							if ($i % 2 == 0) {
								$Data .= chr($suffix % 16);
							} else {
								$Data .= chr(floor($suffix / 16));
							}
							$i++;
						}
					}
					$prefix = freadbyte($f);
					$suffix = freadbyte($f);
					$pocetb += 2;
					if ($echoit)
						echo "Prefix: $prefix Suffix: $suffix<BR>";
				}

				for ($x = 0; $x < strlen($Data); $x++) {
					imagesetpixel($img, $x, $y, $Palette[ord($Data[$x])]);
				}
				$Data = "";
			}
		}

		if ($biBitCount == 24) {
			$img = imagecreatetruecolor($Width, $Height);
			$Zbytek = $Width % 4;

			for ($y = $Height -1; $y >= 0; $y--) {
				for ($x = 0; $x < $Width; $x++) {
					$B = freadbyte($f);
					$G = freadbyte($f);
					$R = freadbyte($f);
					$color = imagecolorexact($img, $R, $G, $B);
					if ($color == -1)
						$color = imagecolorallocate($img, $R, $G, $B);
					imagesetpixel($img, $x, $y, $color);
				}
				for ($z = 0; $z < $Zbytek; $z++)
					freadbyte($f);
			}
		}
		return $img;
	}
	fclose($f);
}

/*
* Helping functions:
*-------------------------
*
* freadbyte($file) - reads 1 byte from $file
* freadword($file) - reads 2 bytes (1 word) from $file
* freaddword($file) - reads 4 bytes (1 dword) from $file
* freadlngint($file) - same as freaddword($file)
* decbin8($d) - returns binary string of d zero filled to 8
* RetBits($byte,$start,$len) - returns bits $start->$start+$len from $byte
* freadbits($file,$count) - reads next $count bits from $file
* RGBToHex($R,$G,$B) - convert $R, $G, $B to hex
* int_to_dword($n) - returns 4 byte representation of $n
* int_to_word($n) - returns 2 byte representation of $n
*/

function freadbyte($f) {
	return ord(fread($f, 1));
}

function freadword($f) {
	$b1 = freadbyte($f);
	$b2 = freadbyte($f);
	return $b2 * 256 + $b1;
}

function freadlngint($f) {
	return freaddword($f);
}

function freaddword($f) {
	$b1 = freadword($f);
	$b2 = freadword($f);
	return $b2 * 65536 + $b1;
}

function RetBits($byte, $start, $len) {
	$bin = decbin8($byte);
	$r = bindec(substr($bin, $start, $len));
	return $r;
}

$CurrentBit = 0;
function freadbits($f, $count) {
	global $CurrentBit;
	$Byte = freadbyte($f);
	$LastCBit = $CurrentBit;
	$CurrentBit += $count;
	if ($CurrentBit == 8) {
		$CurrentBit = 0;
	} else {
		fseek($f, ftell($f) - 1);
	}
	return RetBits($Byte, $LastCBit, $count);
}

function RGBToHex($Red, $Green, $Blue) {
	$hRed = dechex($Red);
	if (strlen($hRed) == 1)
		$hRed = "0$hRed";
	$hGreen = dechex($Green);
	if (strlen($hGreen) == 1)
		$hGreen = "0$hGreen";
	$hBlue = dechex($Blue);
	if (strlen($hBlue) == 1)
		$hBlue = "0$hBlue";
	return ($hRed . $hGreen . $hBlue);
}

function int_to_dword($n) {
	return chr($n & 255) . chr(($n >> 8) & 255) . chr(($n >> 16) & 255) . chr(($n >> 24) & 255);
}
function int_to_word($n) {
	return chr($n & 255) . chr(($n >> 8) & 255);
}

function decbin8($d) {
	return decbinx($d, 8);
}

function decbinx($d, $n) {
	$bin = decbin($d);
	$sbin = strlen($bin);
	for ($j = 0; $j < $n - $sbin; $j++)
		$bin = "0$bin";
	return $bin;
}

function inttobyte($n) {
	return chr($n);
}
?>